package com.qqt.csr.route.timer;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.thread.ThreadUtil;
import com.google.common.collect.Lists;
import com.qqt.csr.common.utils.*;
import com.qqt.csr.route.dto.resp.RouteResultDTO;
import com.qqt.csr.route.entity.RouteDistributionRule;
import com.qqt.csr.route.entity.RouteQueueOverflowCfg;
import com.qqt.csr.route.entity.RouteTask;
import com.qqt.csr.route.enums.RouteTaskEventEnum;
import com.qqt.csr.route.enums.RouteTaskStatusEnum;
import com.qqt.csr.route.service.*;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.MDC;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.*;

@Slf4j
@Component
public class VisitorQueueHandlerTimer implements InitializingBean {
    private static final String LOG_TID = "ctid";
    private volatile boolean isShutdown = false;
    private final ScheduledThreadPoolExecutor scheduledExecutor = ThreadUtil.createScheduledExecutor(1);
    private final ExecutorService workerPool = ThreadPoolUtil.newThreadPoolExecutorCallerRunsRejectPolicy(3, 10, 60, 100, "visitor_queue_prc");

    @Value(value = "${visitor.route.queue.interval:15}")
    private long interval;
    @Value(value = "${visitor.route.queue.limit:10}")
    private int limit;

    @Autowired
    private VisitorQueueService visitorQueueService;
    @Autowired
    private RedissonLockUtil redissonLockUtil;
    @Autowired
    private RouteTaskService routeTaskService;
    @Autowired
    private RouteCsService routeCsService;
    @Autowired
    private RouteMqProducerService routeMqProducerService;
    @Autowired
    private ConversationStatService conversationStatService;
    @Autowired
    private DistributingRouteService distributingRouteService;

    @Override
    public void afterPropertiesSet() throws Exception {
        ThreadUtil.schedule(scheduledExecutor,
                () -> {
                    try {
                        MDC.put(LOG_TID, SnowflakeUtil.nextIdStr());
                        this.lookup();
                    } catch (Throwable e) {
                        log.error("访客队列出队执行异常", e);
                    } finally {
                        MDC.clear();
                    }
                },
                30,
                interval,
                TimeUnit.SECONDS,
                false
        );
    }

    @PreDestroy
    public void destroy() {
        isShutdown = true;
        ThreadPoolUtil.shutdownThreadPoolGracefully(workerPool);
        ThreadPoolUtil.shutdownThreadPoolGracefully(scheduledExecutor);
        log.info("访客队列出队执行已关闭");
    }

    /**
     * 执行出队逻辑
     */
    private void lookup() {
        Date currentTime = new Date();
        List<Long> skillSetIdList = visitorQueueService.getSkillSetQueueList();
        if (CollectionUtils.isEmpty(skillSetIdList)) {
            return;
        }

        List<Future<?>> futures = new ArrayList<>();
        for (Long skillSetId : skillSetIdList) {
            Future<?> future = workerPool.submit(() -> {
                String cacheKey = String.format(CacheKeyUtil.CsrRoute.VISITOR_QUEUE_POLL_LOCK, skillSetId);
                redissonLockUtil.distributedLock(() -> {
                    processSkillSetQueue(skillSetId, currentTime);
                }, cacheKey, 0, 30, TimeUnit.MINUTES);
            });
            futures.add(future);

        }

        // 等待所有任务完成
        for (Future<?> future : futures) {
            try {
                future.get();
            } catch (InterruptedException | ExecutionException e) {
                log.error("处理技能组队列时发生异常", e);
            }
        }
    }

    /**
     * 执行出队逻辑
     */
    private void processSkillSetQueue(Long skillSetId, Date currentTime) {
        int startIndex = 0;
        int endIndex = startIndex + limit;

        List<String> quitQueueTaskIdList = new ArrayList<>();
        List<RouteTask> updateRouteTaskList = new ArrayList<>();
        List<RouteResultDTO> routeResultDTOList = new ArrayList<>();

        int waitNum = 0;
        List<String> routeTaskIdList = visitorQueueService.poll(skillSetId, startIndex, endIndex);
        while (CollectionUtils.isNotEmpty(routeTaskIdList) && !isShutdown) {
            List<RouteTask> routeTaskList = routeTaskService.getListBy(routeTaskIdList);

            for (RouteTask routeTask : routeTaskList) {
                if (!RouteTaskStatusEnum.QUEUE.equals(routeTask.getStatus())) {
                    // 路由任务不在是排队状态，直接退出队列
                    quitQueueTaskIdList.add(routeTask.getId());
                    continue;
                }

                // 如果是首次出队，则设置当前时间为出队时间
                routeTask.setOutQueueTime(Optional.ofNullable(routeTask.getOutQueueTime()).orElse(currentTime));
                routeTask.increaseOutQueueCount();

                RouteResultDTO routeResultDTO = RouteResultDTO.of(routeTask, null, null);

                routeCsService.route(routeTask, routeResultDTO);
                if (RouteTaskStatusEnum.ROUTED.equals(routeTask.getStatus())) {
                    // 出队后，分配客服成功
                    updateRouteTaskList.add(routeTask);
                    routeResultDTO.getRouteTaskDTO().setStatus(routeTask.getStatus());
                    routeResultDTOList.add(routeResultDTO);
                    continue;
                }

                // 依然分配失败
                RouteDistributionRule routeRule = routeTask.getRouteRule();
                // 访客排队时长
                int visitorQueueTimeSec = (int) ((currentTime.getTime() - routeTask.getInQueueTime().getTime()) / 1000);
                if (routeRule.getQueueTimeLimit().compareTo(visitorQueueTimeSec) <= 0) {
                    // 排队时间达到上限，直接退出
                    routeTask.addEvent(RouteTaskEventEnum.OUT_QUEUE_NOT_SEAT);
                    routeResultDTO.setContent(routeRule.getQuitQueueTimeLimitTip());
                    routeResultDTO.getRouteTaskDTO().setStatus(routeTask.getStatus());

                    updateRouteTaskList.add(routeTask);
                    routeResultDTOList.add(routeResultDTO);
                    quitQueueTaskIdList.add(routeTask.getId());
                } else if (routeRule.getQueueTimeout().compareTo(visitorQueueTimeSec) <= 0) {
                    // 达到排队超时，退出当前队列
                    quitQueueTaskIdList.add(routeTask.getId());
                    RouteQueueOverflowCfg routeQueueOverflowCfg = JsonUtil.toObject(routeRule.getQueueTimeoutCfg(), RouteQueueOverflowCfg.class);
                    if (routeQueueOverflowCfg == null || routeRule.getQueueTimeoutType().equals(1)) {
                        // 直接退出路由
                        routeTask.addEvent(RouteTaskEventEnum.OUT_QUEUE_NOT_SEAT);
                        routeResultDTO.setContent(Optional.ofNullable(routeQueueOverflowCfg).map(RouteQueueOverflowCfg::getTip).orElse(routeRule.getQuitQueueTimeLimitTip()));
                        routeResultDTO.getRouteTaskDTO().setStatus(routeTask.getStatus());
                        updateRouteTaskList.add(routeTask);
                        routeResultDTOList.add(routeResultDTO);
                    } else {
                        // 溢出其他组
                        routeQueueOverflowCfg.getSkillSetIdList().remove(skillSetId);
                        Collection<Long> planSkillSetIdList = CollectionUtil.intersectionDistinct(routeQueueOverflowCfg.getSkillSetIdList(), routeTask.getPlanSkillSetIdList());
                        routeTask.setPlanSkillSetIdList(Lists.newArrayList(planSkillSetIdList));
                        // 基于新的规则分发路由
                        distributingRouteService.distribute(routeTask, routeQueueOverflowCfg.getDistributeType());
                        // 移除之前当前技能组统计到会话数量
                        conversationStatService.reduceConversationCount(routeTask.getTenantId(), skillSetId, 1);
                    }
                } else if (currentTime.getTime() > routeTask.getOutQueueTime().getTime() + ((long) routeRule.getQueueTipInterval())) {
                    // 到达播报间隔时间
                    routeResultDTO.setContent(routeRule.buildQueueTip(waitNum));
                    routeResultDTO.getRouteTaskDTO().setStatus(routeTask.getStatus());
                    routeTask.setOutQueueTime(currentTime);
                    updateRouteTaskList.add(routeTask);
                    routeResultDTOList.add(routeResultDTO);
                    waitNum++;
                } else {
                    // 更新最近出队时间
                    routeTask.setOutQueueTime(currentTime);
                    updateRouteTaskList.add(routeTask);
                    waitNum++;
                }
            }

            startIndex = endIndex + 1;
            endIndex = startIndex + limit;
            routeTaskIdList = visitorQueueService.poll(skillSetId, startIndex, endIndex);
        }

        if (CollectionUtils.isNotEmpty(quitQueueTaskIdList)) {
            // 退出排队
            Lists.partition(quitQueueTaskIdList, 100).forEach(pattionList -> {
                visitorQueueService.remove(skillSetId, pattionList);
            });
        }

        if (CollectionUtils.isNotEmpty(updateRouteTaskList)) {
            Lists.partition(updateRouteTaskList, 100).forEach(pattionList -> {
                routeTaskService.save(pattionList);
            });
        }

        if (CollectionUtils.isNotEmpty(routeResultDTOList)) {
            routeResultDTOList.forEach(routeResultDTO -> routeMqProducerService.sendRouteResult(routeResultDTO));
        }
    }


}
